#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */

int set_port(int fd, int baud_rate, int n_bits, char parity, int n_stop)
{
	struct termios options;

	/* 读取配置 */
	if(tcgetattr(fd, &options) < 0) 
	{ 
		perror("SetupSerial 1");
		return -1;
	}

	options.c_iflag = 0x00;
	options.c_oflag = 0x00;
	options.c_cflag = 0x00;
	options.c_lflag = 0x00;

	/* 禁用软件流控 */
	options.c_iflag &= ~(IXON | IXOFF | IXANY);
	if((parity == 'O') || (parity == 'E')) {
		/* 启用输入奇偶校验并剥离奇偶校验位 */
		options.c_iflag |= (INPCK | ISTRIP);
	}

	/* 选择原始输出 */
	options.c_oflag &= ~OPOST;

	/* 设置字符大小 */
	options.c_cflag &= ~CSIZE;
	switch(n_bits)
	{
		case 7:
			options.c_cflag |= CS7;
			break;
		case 8:
		default:
			options.c_cflag |= CS8;
			break;
	}
	/* 设置奇偶校验 */
	switch(parity)
	{
		case 'O':
			options.c_cflag |= PARENB;
			options.c_cflag |= PARODD;
			break;
		case 'E':
			options.c_cflag |= PARENB;
			options.c_cflag &= ~PARODD;
			break;
		case 'N':
		default:
			options.c_cflag &= ~PARENB;
			break;
	}
	/* 禁用硬件流控制 */
	options.c_cflag &= ~CRTSCTS;
	/* 设置停止位 */
	switch(n_stop)
	{
		case 2:
			options.c_cflag |= CSTOPB;
			break;
		case 1:
		default:
			options.c_cflag &= ~CSTOPB;
			break;
	}
	/* 启用接收器，并忽略 modem 控制线 */
	options.c_cflag |= CLOCAL | CREAD; 

	/* 选择原始输入 */
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

	/* 设置波特率 */
	switch(baud_rate)
	{
		case 2400:
			cfsetispeed(&options, B2400);
			cfsetospeed(&options, B2400);
			break;
		case 4800:
			cfsetispeed(&options, B4800);
			cfsetospeed(&options, B4800);
			break;
		case 9600:
			cfsetispeed(&options, B9600);
			cfsetospeed(&options, B9600);
			break;
		case 115200:
		default:
			cfsetispeed(&options, B115200);
			cfsetospeed(&options, B115200);
			break;
	}
	/* 设置读取超时和每次读取的最小字节数 */
	options.c_cc[VMIN] = 1;
	options.c_cc[VTIME] = 1;

	/* 刷新缓冲区 */
	tcflush(fd, TCIFLUSH);

	/* 配置串口 */
	if((tcsetattr(fd, TCSANOW, &options))!=0)
	{
		perror("com set error");
		return -1;
	}

	return 0;
}

int open_port(const char *com)
{
	int fd;

	/* 打开串口 */
	fd = open(com, O_RDWR | O_NOCTTY | O_NDELAY);
	if (fd == -1)
	{
		/* 打开失败 */
		perror("open_port");
	}
	else
	{
		/* 设置为阻塞式读取 */
		fcntl(fd, F_SETFL, 0);
	}

	return (fd);
}

int main(int argc, char **argv)
{
	int fd;
	int result;
	char buffer[64];

	if (argc != 2)
	{
		printf("Usage: \n");
		printf("%s </dev/ttySAC1 or other>\n", argv[0]);
		return -1;
	}

	fd = open_port(argv[1]);
	if (fd < 0)
	{
		printf("open %s err!\n", argv[1]);
		return -1;
	}

	result = set_port(fd, 115200, 8, 'N', 1);
	if(result < 0)
	{
		printf("set port err!\n");
		return -1;
	}

	while(1)
	{
		result = read(fd, &buffer, sizeof(buffer));
		if(result > 0)
		{
			printf(" read %d bytes\r\n", result);
			result = write(fd, &buffer, result);
		}
		else
			perror(NULL);
	}

	return 0;
}

